package boot

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"yunj/pkg/global"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
)

// 初始化数据库连接
func InitDb() {
	dsn := fmt.Sprintf(
		"%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		global.Config.Database.User,
		global.Config.Database.Password,
		global.Config.Database.Host,
		global.Config.Database.Port,
		global.Config.Database.Name,
	)

	// 自定义日志配置
	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // 日志输出目标，这里输出到标准输出
		logger.Config{
			SlowThreshold: time.Second, // 慢查询阈值，超过该时间的查询会被标记为慢查询
			LogLevel:      logger.Info, // 日志级别，可设置为 Silent、Error、Warn、Info
			Colorful:      true,        // 是否开启彩色日志
		},
	)

	// 尝试打开数据库连接
	mysql, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			TablePrefix:   global.Config.Database.TablePrefix, // 设置表名前缀
			SingularTable: true,                               // 禁用表名复数形式
		},
		Logger: newLogger, // 使用自定义日志记录器
	})

	if err != nil {
		panic(fmt.Sprintf("初始化数据库连接失败！%v", err))
	}
	// 获取底层的 sql.DB 对象
	sqlDB, err := mysql.DB()
	if err != nil {
		panic("获取数据库底层sql.DB对象失败: " + err.Error())
	}

	// 连接最大存活时间（必须小于 MySQL 的 wait_timeout）
	// 比服务端wait_timeout小30s，提前淘汰旧连接
	// 例如：若 MySQL wait_timeout=180s，则这里设为 150s
	sqlDB.SetConnMaxLifetime(time.Duration(global.Config.Database.ConnMaxLifetime) * time.Second)
	// 连接最大空闲时间（避免空闲过久的连接被数据库关闭）
	// 空闲连接120s后淘汰，避免被服务端关闭
	sqlDB.SetConnMaxIdleTime(time.Duration(global.Config.Database.ConnMaxIdleTime) * time.Second)
	// 最大空闲连接数（建议与最大连接数一致，避免频繁创建连接）
	// 建议设为MaxOpenConns的50%-80%，减少连接重建
	sqlDB.SetMaxIdleConns(global.Config.Database.MaxIdleConns)
	// 最大打开连接数（根据并发量调整，不宜过大）
	// 小于MySQL max_connections，避免连接耗尽
	sqlDB.SetMaxOpenConns(global.Config.Database.MaxOpenConns)

	// 启用连接健康检查（每次从池获取连接时检测有效性）
	// 注意：该操作会有轻微性能损耗，高并发场景可权衡是否开启
	sqlDB.SetConnMaxIdleTime(180 * time.Second) // 配合健康检查使用

	// 判断版本号
	var versionStr string
	result := mysql.Raw("SELECT VERSION()").Scan(&versionStr)
	if result.Error != nil {
		panic(fmt.Sprintf("获取MySQL版本号异常！%v", err))
	}
	// 提取主版本号（例如：8.0.26）
	versionParts := strings.SplitN(versionStr, ".", 3)
	if len(versionParts) < 2 {
		panic(fmt.Sprintf("无法解析MySQL版本号: %s", versionStr))
	}
	// 转换主版本号为整数
	majorVersion, err := strconv.Atoi(versionParts[0])
	if err != nil {
		panic(fmt.Sprintf("解析MySQL主版本号失败: %v", err))
	}
	if majorVersion < 8 {
		panic("MySQL版本需>=8.0")
	}
	global.MySQL = mysql
}
